Previous Book Contents Book Index Next

Inside Macintosh: /
QuickDraw 3D RAVE


Using QuickDraw 3D RAVE

This section illustrates how to use QuickDraw 3D RAVE. In particular, it provides source code examples that show how you can

These are examples of operations that an application might need to perform. To learn how to write and register a new drawing engine, see the section "Writing a Drawing Engine," beginning on page 1-23.

Note
The code examples shown in this section provide only very rudimentary error handling.

Specifying a Virtual Device

You send all drawing commands to a draw context. To create a draw context, you need to specify a virtual device and a drawing engine. This section shows how to initialize a virtual device. See the next section for information on specifying a drawing engine.

On Macintosh computers, a virtual device represents either an area of memory, a video device, or an offscreen graphics world. You specify a virtual device by filling in fields of a device structure, defined by the TQADevice data type.

typedef struct TQADevice {
   TQADeviceType              deviceType;
   TQAPlatformDevice          device;
} TQADevice;
The deviceType field indicates the type of virtual device you want to draw into. Currently, you can pass either kQADeviceMemory or kQADeviceGDevice to select a Macintosh device type. The device field indicates a platform device data structure, which is either of type TQADeviceMemory for memory devices or GDHandle for graphics devices.

typedef union TQAPlatformDevice {
   TQADeviceMemory            memoryDevice;
   GDHandle                   gDevice;
} TQAPlatformDevice;
To specify a memory device, you fill in the fields of a memory device structure, defined by the TQADeviceMemory data type.

typedef struct TQADeviceMemory {
   long                       rowBytes;
   TQAImagePixelType          pixelType;
   long                       width;
   long                       height;
   void                       *baseAddr;
} TQADeviceMemory;
Listing 1-1 shows how to initialize a memory device.

Listing 1-1 Initializing a memory device

TQADevice   myDevice;
long        myTargetMemory[100][100];

myDevice.deviceType = kQADeviceMemory;
myDevice.device.memoryDevice.rowBytes = 100 * sizeof(long);
myDevice.device.memoryDevice.pixelType = kQAPixel_ARGB32;
myDevice.device.memoryDevice.width = 100;
myDevice.device.memoryDevice.height = 100;
myDevice.device.memoryDevice.baseAddr = myTargetMemory;
Drawing to memory always occurs in the native pixel format of the platform. Note that not all drawing engines support drawing to memory. For information on determining what kinds of virtual devices a particular drawing engine supports, see "Finding a Drawing Engine" (page 1-16).

Listing 1-2 shows how to initialize a virtual graphics device.

Listing 1-2 Initializing a graphics device

TQADevice   myDevice;
GDHandle    gDeviceHandle;

/*create a GDHandle (perhaps by calling NewGDevice)*/
...
myDevice.deviceType = kQADeviceGDevice;
myDevice.device.gDevice = gDeviceHandle;
The code in Listing 1-2 assumes that the gDeviceHandle global variable has been assigned a handle to a GDevice record. See Inside Macintosh: Imaging with QuickDraw for complete information on creating and configuring graphics devices.

Note
A draw context can be associated with only a single virtual device and hence with only a single GDevice. Macintosh windows can straddle several screens, each associated with a different GDevice. It is your responsibility to determine which graphics devices a window straddles and to create a separate draw context for each one.

Finding a Drawing Engine

Not all drawing engines are capable of drawing into all type of virtual devices. For example, some drawing engines might not support memory devices at all, and other drawing engines might support only a particular graphics device. As a result, once you've initialized a virtual device, you need to find a drawing engine that is capable of drawing into that device. You do this by finding the available drawing engines and selecting one that is capable of drawing into the desired virtual device. If more than one engine supports that device, you need to choose one of them.

QuickDraw 3D versions 1.1 and later provide a control panel that allows the user to select the drawing engine to use for each available monitor.

You can search through the list of available drawing engines by calling the QADeviceGetFirstEngine and QADeviceGetNextEngine functions. The QADeviceGetFirstEngine function returns the preferred drawing engine for the specified device; in most cases, this engine is the best engine to use for high performance rendering. However, you might need specific drawing features that are not supported by the preferred drawing engine. If so, you can use the QAEngineGestalt function to query the engine's capabilities, as shown in Listing 1-3.

Listing 1-3 Finding a drawing engine with fast texture mapping

TQAEngine *MyFindPreferredEngine (TQADevice *device)
{
   TQAEngine         *myEngine;
   unsigned long     fast;

   for (myEngine = QADeviceGetFirstEngine(device); 
         myEngine;
         myEngine = QADeviceGetNextEngine(device, myEngine)) {
      if (QAEngineGestalt(myEngine, kQAGestalt_FastFeatures, &fast) == kQANoErr) {
         if (fast & kQAFast_Texture)
            return(myEngine);
      }
   }
   return(NULL);
}
The MyFindPreferredEngine function defined in Listing 1-3 calls the QADeviceGetFirstEngine function to get the preferred drawing engine for the specified device. Then it calls QAEngineGestalt, passing the kQAGestalt_FastFeatures selector, to determine which (if any) features are accelerated by that engine. If the engine supports accelerated texture mapping, the MyFindPreferredEngine function returns that drawing engine. Otherwise, the MyFindPreferredEngine function loops through all engines capable of drawing into the specified device until it finds one that does support fast texture mapping. If none is found, MyFindPreferredEngine returns the value NULL.

Note
See "Gestalt Selectors" (page 1-57) for a complete description of the selectors you can pass to the QAEngineGestalt function.

Creating and Configuring a Draw Context

Once you've initialized a virtual device and selected a drawing engine capable of drawing to that device, you can call the QADrawContextNew function to create a new draw context. You pass the device and engine to that function, along with a drawing rectangle, a clipping region, and a set of draw context flags. The flags specify features of the new draw context. Listing 1-4 illustrates how to create a double-buffered draw context with z buffering.

Listing 1-4 Creating a draw context

TQADrawContext    *myDrawContext;

if (QADrawContextNew(&myDevice, &myRect, &myClip, myEngine, 
         kQAContext_DoubleBuffer, &myDrawContext) != kQANoErr) {
   /*Error! Could not create new draw context.*/
}
If QADrawContextNew succeeds, it returns the result code kQANoErr and sets the myDrawContext parameter to the new draw context. Otherwise, if an error occurs, QADrawContextNew returns some other result code and sets the myDrawContext parameter to the value NULL.

Note
When you are finished using the new draw context, you should free the memory and other resources it uses by calling the QADrawContextDelete function.
QuickDraw 3D RAVE does not provide a function to reposition an existing draw context. If a window associated with a draw context is moved on the screen, you need to delete the existing draw context and create a new draw context at the new location. Similarly, QuickDraw 3D RAVE does not provide a function to change the clipping region of a draw context. If you want to change a clipping region, you need to delete the existing draw context and create a new draw context with the desired clipping region.

However, you can change a number of other features of a draw context without having to delete an existing draw context and create a new one. The features you can change are indicated by the state variables of the draw context. For example, to change the background color of a draw context to opaque black, you can use the code shown in Listing 1-5.

Listing 1-5 Setting a draw context state variable

void MySetBackgroundToBlack (TQADrawContext *drawContext);
{
   QASetFloat(drawContext, kQATag_ColorBG_a, 1.0);
   QASetFloat(drawContext, kQATag_ColorBG_r, 0.0);
   QASetFloat(drawContext, kQATag_ColorBG_g, 0.0);
   QASetFloat(drawContext, kQATag_ColorBG_b, 0.0);
}
The QASetFloat function sets a draw context state variable that has a floating-point value. QuickDraw 3D RAVE provides functions to get and set state variables with floating-point, long integer, or pointer values.

Note
See "Tags for State Variables," beginning on page 1-38, for a complete description of the available draw context state variables.
The QASetFloat function is defined using a C language macro:

#define QASetFloat(drawContext,tag,newValue) \
            (drawContext)->setFloat (drawContext,tag,newValue)
During compilation, the QASetFloat call is replaced by code that directly calls the drawing engine's floating-point setting method. This allows you to achieve the highest possible performance when configuring a draw context.

Drawing in a Draw Context

QuickDraw 3D RAVE allows you to draw five kinds of objects in a draw context: points, lines, triangles, triangle meshes, and bitmaps. You draw by calling a function to draw the desired type of object. For instance, to draw a single point, you can call the QADrawPoint function, as follows:

QADrawPoint(myDrawContext, myPoint);
Here, the myPoint parameter specifies the point to draw. All objects that a drawing engine can draw (except for bitmaps) are defined by points or vertices. QuickDraw 3D RAVE supports two different types of vertices: Gouraud vertices and texture vertices. You use Gouraud vertices for drawing Gouraud-shaded triangles, and also for drawing points and lines. A Gouraud vertex is defined by the TQAVGouraud data structure, which specifies the position, depth, color, and transparency information.

You use texture vertices to define triangles to which a texture is to be mapped. A texture vertex is defined by the TQAVTexture data structure, which specifies the position, depth, transparency, and texture mapping information.

IMPORTANT
QuickDraw 3D RAVE does not currently support clipping to a draw context. All triangles and other objects drawn to a draw context must lie entirely within the draw context.

Using a Draw Context as a Cache

QuickDraw 3D RAVE supports draw context caching, a technique that allows you to improve rendering performance when a large number of the objects in a scene don't change from frame to frame. A draw context cache is simply a draw context that contains an image and is designated as the initial context in a call to QARenderStart. The contents of that context are drawn into the destination draw context before any other objects.

To create a draw context cache, you first create a draw context by calling the QADrawContextNew function, where the flags parameter has the QAContext_Cache flag set. Then you draw the unchanging objects into the draw context cache. For example, suppose that you want to draw a series of frames in which two triangles remain constant from frame to frame but a third triangle changes every frame. Listing 1-6 shows how to do this.

Listing 1-6 Creating and using a draw context cache

TQAVGouraud          tri1[3], tri2[3], tri3[3];
TQADrawContext       *myCache, *myDest;

/*Create draw context cache and destination draw context.*/
QADrawContextNew(myDev, rect, NULL, myEng, QAContext_Cache, &myCache);
QADrawContextNew(myDev, rect, NULL, myEng, QAContext_DoubleBuffer, &myDest);

/*Set up the image in the cache context.*/
QARenderStart(myCache, NULL, NULL);
QADrawTriGouraud(myCache, &tri1[0], &tri1[1], &tri1[2], kQATriFlags_None);
QADrawTriGouraud(myCache, &tri2[0], &tri2[1], &tri2[2], kQATriFlags_None);
QARenderEnd(myCache, NULL);

/*Render frames using the cache and moving tri3 only.*/
while (gStillMovingTriangle3) {
   MyMoveTri(tri3);
   QARenderStart(myDest, NULL, myCache);
   QADrawTriGouraud(myDest, &tri3[0], &tri3[1], &tri3[2], kQATriFlags_None);
   QARenderEnd(myDest, NULL);
}
Not all drawing engines support draw context caching. If a drawing engine does not support caching, it should return the value NULL whenever you pass the QAContext_Cache flag to QADrawContextNew.

IMPORTANT
All draw context caches must be single buffered, and they must be created using the same device and rectangle as the destination draw contexts with which they will be used.

Using a Texture Map Alpha Channel

Texture maps whose pixel type is either kQAPixel_ARGB16 or kQAPixel_ARGB32 contain an alpha channel value for each pixel in the map. You can use the alpha channel value to control the transparency of an object on a pixel-by-pixel basis, or you can use the alpha channel value as a blend matte that exposes only certain portions of an image.

To use the alpha channel to control transparency, you should set the drawing engine's transparency blending mode to kQABlend_Premultiply. (You specify an engine's transparency blending mode by assigning a value to its kQATag_Blend state variable.) For pixels of type kQAPixel_ARGB16, the alpha channel value occupies bit 15; when the value is 1, the pixel is opaque; when the value is 0, the pixel is completely transparent. For pixels of type kQAPixel_ARGB32, the alpha channel value occupies bits 31 through 24; when the value is 255, the pixel is opaque; when the value is 0, the pixel is completely transparent.

IMPORTANT
The kQABlend_Premultiply transparency model assumes that the diffuse color of a pixel has been premultiplied by the alpha channel value. As a result, every pixel of the texture map must be premultiplied by its associated alpha channel value before you create the texture map by calling QATextureNew.
Note that the specular highlight is unaffected by the diffuse transparency of an object. As a result, setting an object's alpha channel value to 0 when using the kQABlend_Premultiply transparency blending mode does not cause the object to vanish. The specular highlight is still rendered.

To use the alpha channel as a blend matte to cut out certain portions of a rendered object, you should set the drawing engine's transparency blending mode to kQABlend_Interpolate. If the alpha channel value of all pixels in an object is 0, neither the object nor its specular highlight is rendered. This effectively eliminates the object from the rendered image.

IMPORTANT
The kQABlend_Interpolate transparency model assumes that the diffuse color of a pixel has not been premultiplied by the alpha channel value. This multiplication is performed by the blending operation.
The kQABlend_Interpolate blending mode cannot render a transparent surface as accurately as the kQABlend_Premultiply mode, because the specular highlight is scaled by the alpha value. In some cases, you can compensate for this behavior by increasing the brightness of the specular highlight.

Rendering With Antialiasing

A drawing engine may support an antialiasing mode that determines the kind of antialiasing applied to a drawing context. (Antialiasing is the smoothing of jagged edges on a displayed shape by modifying the transparencies of individual pixels along the shape's edge.)

Note
The antialiasing mode state variable is optional; it must be supported only when a drawing engine supports the kQAOptional_Antialias feature.
You specify an engine's antialiasing mode by assigning a value to its kQATag_Antialias state variable. QuickDraw 3D RAVE provides these constants for antialiasing modes:

#define kQAAntiAlias_Off               0
#define kQAAntiAlias_Fast              1
#define kQAAntiAlias_Mid               2
#define kQAAntiAlias_Best              3
The interpretation of these values is specific to each drawing engine. For example, a drawing engine might be able to support antialiased line drawing with no performance penalty but that same engine might incur a 50 percent slowdown when drawing antialiased triangles. Accordingly, this engine might interpret the kQAAntiAlias_Fast antialiasing mode as rendering antialiased lines only, and it might interpret the kQAAntiAlias_Mid mode as rendering both antialiased lines and triangles.

Note
QuickDraw 3D RAVE interprets antialiasing modes independently of the transparency blending modes, unlike some other rendering technologies. For instance, with OpenGL you must select specific blending modes when antialiasing is enabled.

Previous Book Contents Book Index Next

© Apple Computer, Inc.
28 AUG 1996